#include <fstream>
#include <iostream>
#include <list>
#include <queue>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
#include <algorithm>

#include "framework/ASTManager.h"
#include "framework/BasicChecker.h"
#include "framework/CallGraph.h"
#include <clang/AST/AST.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Expr.h>
#include <clang/AST/RecordLayout.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/ASTConsumers.h>
#include <clang/Frontend/ASTUnit.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Support/raw_ostream.h>

#include "framework/Report.h"

using namespace clang;
using namespace llvm;
using namespace clang::driver;
using namespace clang::tooling;
using namespace std;

class MemoryPointer {
public:
    string name;
    string field;
    int lineno;
    int colno;  
    string type;    //param,return

    MemoryPointer(string name) {
      this->name = name;
    } 

    MemoryPointer(MemoryPointer* mp) : name(mp->name),
    field(mp->field),
    lineno(mp->lineno),
    colno(mp->colno),
    type(mp->type) {}
};

class MemoryAddr {
public:
    int id;
    int type;
    string field;
    int lineno;
    int colno;
    bool isarray;
    bool isparam;
    bool isreleased;
    bool isreturned;
    bool isjudged;
    bool beforejudged;
    int num;   

    MemoryAddr(int id) {
      this->id = id;
      this->num = 1;
      this->isarray = false;
      this->isparam = false;
      this->isreleased = false;
      this->isreturned = false;
      this->isjudged = false;
      this->beforejudged = false;
    } 

    MemoryAddr(MemoryAddr* ma) : id(ma->id),
    type(ma->type),
    field(ma->field),
    lineno(ma->lineno),
    colno(ma->colno),
    isarray(ma->isarray),
    isparam(ma->isparam),
    isreleased(ma->isreleased),
    isreturned(ma->isreturned),
    isjudged(ma->isjudged),
    beforejudged(ma->beforejudged),
    num(ma->num) {}
};

class MemoryAddrKeyHash {
public:
  std::size_t operator()(const MemoryAddr& addr) const
  {
    return std::hash<std::string>()(addr.field) ^
      (std::hash<int>()(addr.colno) << 1) ^
      (std::hash<int>()(addr.lineno) << 2);
  }
};

class MemoryAddrKeyEqual {
public:
  bool operator()(const MemoryAddr& addrLhs,const MemoryAddr& addrRhs) const
  {
    return addrLhs.field == addrRhs.field
    && addrLhs.lineno == addrRhs.lineno
    && addrLhs.colno == addrRhs.colno;
  }
};

class FuncMemory {
public:
  FunctionDecl* F;
  map<std::string,std::vector<MemoryAddr*>> pointerSet;
  map<int,MemoryAddr*>mem;
  map<std::string,MemoryPointer*>pointers;

  FuncMemory(FunctionDecl* F){
    this->F = F;
  }

  FuncMemory(FuncMemory* f){
    this->F = f->F;

    for(auto mp = f->pointers.begin();
    mp != f->pointers.end();mp++){
      this->pointers.insert(
        pair<std::string,MemoryPointer*>(mp->first,new MemoryPointer(mp->second)));
    }

    for(auto mi = f->mem.begin();
    mi != f->mem.end();mi++){
      this->mem.insert(pair<int,MemoryAddr*>(mi->first,new MemoryAddr(mi->second)));
    }

    for(auto ps = f->pointerSet.begin();
    ps != f->pointerSet.end();ps++){
      std::vector<MemoryAddr*> madr;
      for(auto madr_iter = ps->second.begin();
      madr_iter != ps->second.end();madr_iter++){
        if(this->mem.count((*madr_iter)->lineno) == 0){
          this->mem.insert(pair<int,MemoryAddr*>(
            (*madr_iter)->lineno,new MemoryAddr((*madr_iter))));          
        }
        madr.push_back(this->mem[(*madr_iter)->lineno]);
      }
      this->pointerSet.insert(pair<std::string,std::vector<MemoryAddr*>>(ps->first,madr));
    }
  }
};

class MemoryLeakCheck : public BasicChecker {
public:
  MemoryLeakCheck(ASTResource *resource, ASTManager *manager,
                     CallGraph *call_graph, Config *configure)
      : BasicChecker(resource, manager, call_graph, configure){};
  void check(std::string blockName);

private:
  enum BranchStatus{
    INIT,
    ALL_FREE,
    NO_FREE,
    POSSIBLE_FREE
  };

  void readConfig(std::string blockName);
  void traverseStmt(const Stmt *stmt);
  void traverseCallFunc(FunctionDecl *func_decl,map<int,string> paramPtr,string dstName);
  void traverseCallExpr(const Stmt* stmt);

  std::vector<std::string> allocFunc;
  std::vector<std::string> freeFunc;
  std::string interprocedural_analysis;
  std::string functionpointer_analysis;

  map<FunctionDecl*,FuncMemory*> MemoryFuncList;
  map<FunctionDecl*,bool> FuncVisit;
  map<FunctionDecl*,bool> FuncHasFp;
  map<FunctionDecl*,vector<std::string>> FuncFp;
  map<std::string,FunctionDecl*> FmName;

  FuncMemory* onePath;
  map<int,int> param_addr_type;

  std::unordered_map<MemoryAddr,ReportSpace::Defect *,MemoryAddrKeyHash,MemoryAddrKeyEqual> leakMap;
  std::unordered_map<std::string,bool> leakHas;

  vector<ReportSpace::Defect *> leakReport;
  vector<ReportSpace::Defect *> freeReport;

  std::string BLOCKNAME;
  std::string FIELD;
  ReportSpace::DefectName LEAKTYPE;
  ReportSpace::DefectName FREETYPE;
  int COL;
  int LINE;

  void getVar(const Stmt* stmt,int& id);
  void analyzeDecl(const Stmt* stmt,const VarDecl*vd);

  std::string getString_of_Expr(Expr *expr);
  std::string getString_of_Stmt(Stmt *stmt);
  std::vector<string> split_config_func(string tmp);
  std::string split_call_of_Expr(Expr *expr);

  bool isFunctionPointer(VarDecl *D);
  bool isPointerOrReferenceType(const clang::Type* T);
  bool hasSameReport(ReportSpace::Defect * report,int type);
  bool findMadr(map<MemoryAddr*,BranchStatus> state,MemoryAddr* madr);
  bool findSameMem(std::vector<MemoryAddr*> curm,MemoryAddr* madr);
  MemoryAddr* getMadr(map<MemoryAddr*,BranchStatus> state,MemoryAddr* madr);
  void addLeakReport(std::string field,int line);
  bool inLeakReport(std::string field,int line);

  bool IgnoreFunc(ASTFunction *F);
  void updateFuncMemory(FuncMemory* dst,
  const FunctionDecl *func_decl,map<int,string> paramPtr,string dstName);
  //std::vector<FunctionDecl*> getFuncdeclWithFunctionPointer(const CallExpr*,const Stmt *stmt);
  FunctionDecl* getFuncdeclWithFunctionPointer(const CallExpr*,const Stmt *stmt);
  
  void handleFunctionDecl(FunctionDecl *FD);
  void enterFunctionScope(FunctionDecl *FD);
  void handleParam(FunctionDecl *FD);
  void handleNoFree();
  void handleNoElse();
  void handleFreeState(int ifLevel,MemoryAddr* madr);
  void handleLastCase();
  void handleDefault();
  void handleReturn();
  void handleFunctionEnd();
  void handleLoopEnd();
  void handleFunctionPointer(FunctionDecl *FD);
  void finishFunctionScope(FunctionDecl *FD);
  void handleStatement(const clang::Stmt *S,SourceManager&srcMgr);
  void handleIfCond(const clang::IfStmt *IS);
  void enterIfScope(const clang::IfStmt *IS);
  void enterElseScope(const clang::IfStmt *IS);
  void enterElseIfScope(const clang::IfStmt *IS);
  void enterSwitchScope(const clang::SwitchStmt *SS);
  void enterCaseStmt(const clang::CaseStmt *CS);
  void enterDefaultStmt(const clang::DefaultStmt *DS);
  void enterWhileScope(const clang::WhileStmt *WS);
  void enterDoScope(const clang::DoStmt *DS);
  void enterForScope(const clang::ForStmt *FS);
  void finishIfScope(const clang::IfStmt *IS);
  void finishElseIfScope(const clang::IfStmt *IS);
  void finishSwitchScope(const clang::SwitchStmt *SS);
  void finishWhileScope(const clang::WhileStmt *WS);
  void finishDoScope(const clang::DoStmt *DS);
  void finishForScope(const clang::ForStmt *FS);
  void mergeFlow();

  int ifLevel;
  bool inIf;
  bool inElseIf;
  bool inFirstIf;
  bool inFirstCase;
  bool inReturn;
  map<int,FuncMemory*> onePathTmp;
  map<int,std::vector<FuncMemory*>> ifStates;
  map<int,map<MemoryAddr*,BranchStatus>> madrStates;
  map<int,bool> hasDefault;

  int forLevel;
  map<int,FuncMemory*> forPathTmp;

  void updateFunctionPointer(ASTFunction* F);
  map<FunctionDecl*,std::vector<std::pair<ASTFunction *, int64_t>>> funPointerStates;
  map<FunctionDecl*,std::vector<FunctionDecl*>> callstmtStates;
  void enterMultiFunc();
  void handleLastFunc();
  void enterCurFunc();
  void finishMultiFunc();
};